package com.varian.service.aspect;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.varian.redis.util.RedisUtil;
import com.varian.security.util.SecurityUtil;
import com.varian.service.annotation.Resubmit;
import com.varian.service.constant.ServiceConstant;
import com.varian.tool.Func;
import com.varian.tool.contant.ResultConstant;
import com.varian.tool.exception.ServerException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.redisson.api.RLock;

import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author ben
 * @since 2024/9/13
 */
@Slf4j
@Aspect
public class ResubmitAspect {

    @Pointcut("@within(com.varian.service.annotation.Resubmit) || @annotation(com.varian.service.annotation.Resubmit)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Resubmit resubmit = Func.pointcutGetAnnotation(joinPoint, Resubmit.class);
        RLock lock = null;
        try {
            Map<String, Object> map = MapUtil.newTreeMap(CharSequence::compare);
            map.put("user-id", SecurityUtil.getUserIdOnSafe());
            map.put("application-name", SpringUtil.getApplicationName());
            map.put("class-name", joinPoint.getTarget().getClass().getName());
            map.put("method-name", joinPoint.getSignature().getName());
            map.put("request-ip", Func.getClientIp());
            map.put("params", joinPoint.getArgs());
            String md5 = SecureUtil.signParamsMd5(map, "resubmit");
            lock = RedisUtil.getLock(ServiceConstant.RESUBMIT_LOCK.format(md5));
            boolean expression = lock.tryLock(0, resubmit.value(), TimeUnit.SECONDS);
            Assert.isTrue(expression, () -> ServerException.of(ResultConstant.FAIL.status(), "请勿重复提交，请稍后再试"));
            return joinPoint.proceed();
        } finally {
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}